package com.beiding.service.email;

import com.beiding.config.EmailAmqpConfig;
import com.beiding.dao.KeyValueDao;
import com.beiding.exception.RegisterException;
import com.beiding.pojo.Email;
import com.beiding.pojo.manager.Application;
import com.beiding.service.Authorities;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.*;

@Service
public class EmailServiceImpl implements EmailService {

    private PasswordEncoder passwordEncoder;

    //键值对dao
    private KeyValueDao keyValue;

    private RabbitTemplate rabbitTemplate;


    @Autowired
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Autowired
    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    @Autowired
    public void setKeyValue(KeyValueDao keyValue) {
        this.keyValue = keyValue;
    }


    @Override
    public void checkCode(String email, String code) {

        CodeHolder holder = (CodeHolder) keyValue.get(email + "code");

        if (holder == null) {
            throw new RegisterException("code", "未获取验证码或已过期");
        }

        if (!code.equals(holder.code)) {

            holder.errorTime++;

            //如果错误次数大于五次,验证码失效
            if (holder.errorTime > 5) {
                invalidateCode(email);
                throw new RegisterException("code", "验证码已失效,请重新获取");
            } else {

                //更新验证码信息

                long t = 300 - (System.currentTimeMillis() - holder.createTime) / 1000;
                //如果剩余的时间少于5s,验证码也将失效
                if (t < 5) {
                    invalidateCode(email);
                    throw new RegisterException("code", "验证码已失效,请重新获取");
                }

                //更新验证码
                keyValue.put(email + "code", holder, t);

                throw new RegisterException("code", "验证码不匹配");
            }
        }
    }


    @Override
    public void sendCode(String email) {//这里认为邮箱格式都是正确的,在controller层进行校验

        //生成一个验证码
        CodeHolder holder = new CodeHolder();
        holder.code = createCode();

        //验证码有效期五分钟.使用邮件地址作为key
        keyValue.put(email + "code", holder, 300);

        //发送验证码邮件
        rabbitTemplate.convertAndSend(EmailAmqpConfig.SEND_EMAIL_QUEUE, new Email(email, holder.code, "验证码"));
    }

    @Override
    public void invalidateCode(String email) {
        keyValue.remove(email + "code");
    }


    private static class CodeHolder implements Serializable {

        String code;
        Integer errorTime = 0;
        Long createTime = System.currentTimeMillis();

    }


    private String currentContextPath() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getRequest();


        String url = request.getRequestURL().toString();

        return url.split(request.getServletPath())[0];

    }

    @Override
    public void sendRetrievePassword(String email, String username) {

        //用户名加一个随机数
        String token = toToken(username + "rp" + createCode());

        //持久化,一个小时有效
        keyValue.put(token, username, 3600);

        String path = currentContextPath() + "/user/retrieve-password/new?token=" + token;

        rabbitTemplate.convertAndSend(EmailAmqpConfig.SEND_EMAIL_QUEUE, new Email(email, path, "密码找回"));

    }

    @Override
    public String getUsernameByToken(String token) {
        return (String) keyValue.get(token);
    }

    @Override
    public void invalidateToken(String token) {
        keyValue.remove(token);
    }


    //申请权限的邮件模板
    private final String eTemplate = "<html class=\" -webkit- js flexbox canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths\">\n" +
            "<head>\n" +
            "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" +
            "    <meta name=\"viewport\" content=\" initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n" +
            "    <meta name=\"format-detection\" content=\"telephone=no\">\n" +
            "    <title>权限申请</title>\n" +
            "\n" +
            "    <style>\n" +
            "\n" +
            "\n" +
            "    </style>\n" +
            "<body>\n" +
            "\n" +
            "<div style='    width: 100%;\n" +
            "            height: 100%;\n" +
            "            background-color: gainsboro;\n" +
            "        '>\n" +
            "\n" +
            "    <table style='    width: 80%;\n" +
            "            margin-left: 10%;\n" +
            "        '>\n" +
            "        <tbody>\n" +
            "        <tr>\n" +
            "            <td>申请人:</td>\n" +
            "            <td>___proposer___</td>\n" +
            "        </tr>\n" +
            "        <tr>\n" +
            "            <td>所申请权限:</td>\n" +
            "            <td>___authority___</td>\n" +
            "        </tr>\n" +
            "        <tr>\n" +
            "            <td>申请正文:</td>\n" +
            "            <td>___text___</td>\n" +
            "        </tr>\n" +

            "        <tr>\n" +
            "            <td>授权链接:</td>\n" +
            "            <td>___path___</td>\n" +
            "        </tr>\n" +

            "        </tbody>\n" +
            "    </table>\n" +
            "</div>\n" +
            "\n" +
            "</body>\n" +
            "</html>\n";

    @Override
    public void sendApplyForManager(String email, String name, GrantedAuthority authority, String text) {


        //生成令牌
        String token = toToken(name + "afm" + authority + createCode());

        Application application = new Application();

        application.setAuthority(authority);

        application.setProposer(name);

        //存储一周的时间
        keyValue.put(token, application, 3600 * 24 * 7);

        String path = currentContextPath() + "/manager/accredit?token=" + token;

        String et = eTemplate;
        et = et.replace("___proposer___", name);
        et = et.replace("___authority___", Authorities.toText(authority));
        et = et.replace("___path___", path);
        et = et.replace("___text___", text);

        rabbitTemplate.convertAndSend(EmailAmqpConfig.SEND_EMAIL_QUEUE, new Email(email, et, "申请权限"));

    }


    //将原始数据生成令牌
    private String toToken(String raw) {

        //将原始数据进行乱序
        List<Character> list = new ArrayList<>();
        char[] array = raw.toCharArray();
        for (char c : array) {
            list.add(c);
        }
        Collections.shuffle(list, random);
        StringBuilder s = new StringBuilder();
        for (char c : list) {
            s.append(c);
        }

        //加密处理,然后再base64转码
        return new String(Base64.getEncoder().encode(passwordEncoder.encode(s).getBytes()));

    }


    @Override
    public Application getApplication(String token) {
        Object o = keyValue.get(token);
        if (o == null) {
            return null;
        }
        try {
            return (Application) o;
        } catch (Exception ignore) {
            return null;
        }

    }

    @Override
    public void send(String email, String topic, String text) {
        rabbitTemplate.convertAndSend(EmailAmqpConfig.SEND_EMAIL_QUEUE, new Email(email, text, topic));
    }

    private Random random = new Random();

    private DecimalFormat df = new DecimalFormat("000000");

    private String createCode() {
        return df.format(random.nextInt(999999));
    }

}

